﻿using System;
using System.IO;
using System.Linq;
using System.Text;
using System.Text.RegularExpressions;
using System.Xml;
using System.Xml.Linq;
using System.Xml.Serialization;

namespace ServiceHelper
{
    public class XmlUtils
    {
        /// <summary>
        /// Get default XmlWriterSetting using in this util
        /// </summary>
        /// <returns></returns>
        private static XmlWriterSettings GetWriterDefaultSetting()
        {
            return new XmlWriterSettings()
            {
                OmitXmlDeclaration = false,
                NewLineOnAttributes = false
            };
        }

        /// <summary>
        /// Get default xml reader setting
        /// </summary>
        /// <returns></returns>
        private static XmlReaderSettings GetReaderDefaultSetting()
        {
            return new XmlReaderSettings()
            {
                IgnoreComments = true,
            };
        }

        /// <summary>
        /// Parse XML Stream to Object which using Unicode Encoding
        /// </summary>
        /// <typeparam name="T"></typeparam>
        /// <param name="data"></param>
        /// <returns></returns>
        public static T ToObject<T>(Stream data) where T : class, new()
        {
            return ToObject<T>(data, Encoding.Unicode);
        }

        /// <summary>
        /// Parse To Object from XmlReader
        /// </summary>
        /// <typeparam name="T"></typeparam>
        /// <param name="reader"></param>
        /// <returns></returns>
        public static T ToObject<T>(XmlReader reader) where T : class, new()
        {
            try
            {
                XmlSerializer ser = new XmlSerializer(typeof(T));
                return (T)ser.Deserialize(reader);
            }
            catch
            {
                return null;
            }
        }

        /// <summary>
        /// Parse XML Stream to Object using specific encoding
        /// </summary>
        /// <typeparam name="T"></typeparam>
        /// <param name="data"></param>
        /// <returns></returns>
        public static T ToObject<T>(Stream data, Encoding encoding) where T : class, new()
        {
            if (data == null)
                return null;

            try
            {
                XmlSerializer ser = new XmlSerializer(typeof(T));
                return (T)ser.Deserialize(new StreamReader(data, Encoding.Unicode));
            }
            catch
            {
                return null;
            }
        }

        /// <summary>
        /// Parse XML String to Object which using Unicode encoding
        /// </summary>
        /// <typeparam name="T"></typeparam>
        /// <param name="data"></param>
        /// <returns></returns>
        public static T ToObject<T>(String data) where T : class, new()
        {
            return ToObject<T>(data, Encoding.Unicode);
        }

        /// <summary>
        /// Parse XML String to Object using specific encoding
        /// </summary>
        /// <typeparam name="T"></typeparam>
        /// <param name="data"></param>
        /// <returns></returns>
        public static T ToObject<T>(String data, Encoding encoding) where T : class, new()
        {
            if (data == null)
                return null;
            try
            {
                // Init memory stream
                var ms = new MemoryStream(encoding.GetBytes(data));

                T result = ToObject<T>(ms, encoding);

                // Close Stream
                ms.Close();

                // now return
                return result;
            }
            catch
            {
                return null;
            }
        }

        /// <summary>
        /// Parse content in file to Object using specific encoding
        /// </summary>
        /// <typeparam name="T"></typeparam>
        /// <param name="filePath"></param>
        /// <param name="encoding"></param>
        /// <returns></returns>
        public static T ToObjectFromFile<T>(String filePath, Encoding encoding) where T : class, new()
        {
            try
            {
                // Setting first
                XmlReaderSettings setting = GetReaderDefaultSetting();

                // File Stream
                FileStream fs = File.Open(filePath, FileMode.Open);
                XmlReader xmlReader = XmlReader.Create(new StreamReader(fs, encoding), setting);

                // now return result
                T result = ToObject<T>(xmlReader);

                // Close Streams
                fs.Close();

                return result;
            }
            catch
            {
                return null;
            }
        }

        /// <summary>
        /// Parse content in file to Object using Unicode Encoding
        /// </summary>
        /// <typeparam name="T"></typeparam>
        /// <param name="filePath"></param>
        /// <param name="encoding"></param>
        /// <returns></returns>
        public static T ToObjectFromFile<T>(String filePath) where T : class, new()
        {
            return ToObjectFromFile<T>(filePath, Encoding.Unicode);
        }

        /// <summary>
        /// Parse Serializable Object to String using Unicode Encoding
        /// </summary>
        /// <typeparam name="T"></typeparam>
        /// <param name="data"></param>
        /// <returns></returns>
        public static String ToXMLString<T>(T data)
        {
            return ToXMLString<T>(data, Encoding.Unicode);
        }

        /// <summary>
        /// Parse Serializable Object to String using specific encoding
        /// </summary>
        /// <typeparam name="T"></typeparam>
        /// <param name="data"></param>
        /// <returns></returns>
        public static String ToXMLString<T>(T data, Encoding encoding)
        {
            if (data == null)
                return null;
            try
            {
                XmlSerializer ser = new XmlSerializer(typeof(T));
                var stream = new MemoryStream();

                // Setting
                XmlWriterSettings setting = GetWriterDefaultSetting();
                setting.Encoding = encoding;

                // Now do serialize
                ser.Serialize(XmlWriter.Create(stream, setting), data);

                // Write to string
                String result = encoding.GetString(stream.ToArray());

                // Close Stream
                stream.Close();

                // Now return
                return result;
            }
            catch
            {
                return null;
            }
        }

        /// <summary>
        /// Write Serializabe Object to File using Unicode Encoding
        /// </summary>
        /// <typeparam name="T"></typeparam>
        /// <param name="data"></param>
        /// <param name="filePath"></param>
        /// <returns></returns>
        public static bool ToFile<T>(T data, String filePath)
        {
            return ToFile<T>(data, Encoding.Unicode, filePath);
        }

        /// <summary>
        /// Write Serializabe Object to File using specific Encoding
        /// </summary>
        /// <typeparam name="T"></typeparam>
        /// <param name="data"></param>
        /// <param name="encoding"></param>
        /// <param name="filePath"></param>
        /// <returns></returns>
        public static bool ToFile<T>(T data, Encoding encoding, String filePath)
        {
            if (data == null)
                return false;

            try
            {
                XmlSerializer ser = new XmlSerializer(typeof(T));

                // Setting
                XmlWriterSettings setting = GetWriterDefaultSetting();
                setting.Encoding = encoding;

                // Delete file first
                FileInfo finfo = new FileInfo(filePath);
                finfo.Delete();

                // File Stream
                FileStream fs = File.Open(filePath, FileMode.OpenOrCreate, FileAccess.Write);

                // Now do serialize
                ser.Serialize(XmlWriter.Create(fs, setting), data);
                fs.Close();

                // now return
                return true;
            }
            catch
            {
                return false;
            }
        }

        /// <summary>
        /// Remove all Namespace
        /// Created by: Peter Stegnar from StackOverFlow.com
        /// </summary>
        /// <param name="xmlDocument"></param>
        /// <returns></returns>
        public static string RemoveAllNamespaces(string xmlDocument)
        {
            if (string.IsNullOrEmpty(xmlDocument))
                return String.Empty;

            try
            {
                XElement xmlDocumentWithoutNs = RemoveAllNamespaces(XElement.Parse(xmlDocument));

                return xmlDocumentWithoutNs.ToString();
            }
            catch
            {
                return null;
            }
        }

        //Core recursion function
        private static XElement RemoveAllNamespaces(XElement xmlDocument)
        {
            if (!xmlDocument.HasElements)
            {
                XElement xElement = new XElement(xmlDocument.Name.LocalName);
                xElement.Value = xmlDocument.Value;
                return xElement;
            }
            return new XElement(xmlDocument.Name.LocalName, xmlDocument.Elements().Select(el => RemoveAllNamespaces(el)));
        }

        /// <summary>
        /// Remove all the xml namespaces (xmlns) attributes in the xml string
        /// Source: Java2s.com (http://www.java2s.com/Code/CSharp/XML/Removeallthexmlnamespacesxmlnsattributesinthexmlstring.htm)
        /// </summary>
        /// <param name="xmlData"></param>
        /// <returns></returns>
        public static string RemoveAllXmlNamespace(string xmlData)
        {
            string xmlnsPattern = "\\s+xmlns\\s*(:\\w)?\\s*=\\s*\\\"(?<url>[^\\\"]*)\\\"";
            MatchCollection matchCol = Regex.Matches(xmlData, xmlnsPattern);

            foreach (Match m in matchCol)
            {
                xmlData = xmlData.Replace(m.ToString(), String.Empty);
            }
            return xmlData;
        }
    }
}